package com.net.web.server.server;

import com.net.common.util.CloseableUtils;
import com.net.common.util.SnowflakeIdGenerator;
import com.net.common.util.ThreadPoolFactory;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static com.net.web.server.config.ServerMapContext.*;

/**
 * 负责为客户端监听浏览器请求
 *
 * @author hjh
 */
@Slf4j
public class ListenerProxySocketRunnable implements Runnable {
    /**
     * 客户端socket
     */
    private final Socket clientSocket;
    /**
     * 客户端标识
     */
    private final Long sessionId;
    /**
     * 代理端口
     */
    private final int proxyPort;

    public ListenerProxySocketRunnable(Socket clientSocket, Long sessionId, int proxyPort) {
        this.clientSocket = clientSocket;
        this.sessionId = sessionId;
        this.proxyPort = proxyPort;
    }

    @Override
    public void run() {
        ServerSocket listenerProxySocket;
        try {
            listenerProxySocket = new ServerSocket(proxyPort);
            proxyServerSocketMap.put(sessionId, listenerProxySocket);
            while (true) {
                Socket socketWan = listenerProxySocket.accept();
                socketWan.setSoTimeout(1000 * 60);
                ThreadPoolFactory.execute(new SocketWanRunnable(socketWan, clientSocket, sessionId, addSocketWanMap(socketWan)));
            }
        } catch (IOException e) {
            log.info("代理服务{}已关闭", proxyPort);
        } finally {
            // 删除缓存并且释放
            CloseableUtils.close(proxyServerSocketMap.remove(sessionId), clientSocketMap.remove(sessionId));
        }
    }

    /**
     * 浏览器socket加入缓存
     *
     * @param socketWan 浏览器socket
     * @return socketWanId
     */
    private Long addSocketWanMap(Socket socketWan) {
        Long socketWanId = SnowflakeIdGenerator.getId();
        if (Objects.isNull(socketWanMap.get(sessionId))) {
            Map<Long, Socket> socketMap = new ConcurrentHashMap<>(16);
            socketMap.put(socketWanId, socketWan);
            socketWanMap.put(sessionId, socketMap);
        } else {
            Map<Long, Socket> socketMap = socketWanMap.get(sessionId);
            socketMap.put(socketWanId, socketWan);
        }
        return socketWanId;
    }
}
